/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Network;

import Graph.BarnesHutGraphDrawer;
import Network.NetworkDrawer;
import data.*;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import javax.swing.JButton;
import perspectives.Property;
import perspectives.PropertyManager;
import util.Points2DViewer;
import Graph.*;
import org.w3c.dom.Text;

/**
 *
 * @author mokoe001
 */
public class NetworkViewer extends NetworkDrawer {
    private int pickedEdge =-1;

    public NetworkViewer(String name, TableData tab) {
        super(name, tab);
        createAdditionalProperties();
    }

    public void createAdditionalProperties() {

        try {

            PropertyManager.OpenFile ff = new PropertyManager.OpenFile();
            Property<PropertyManager.OpenFile> p33 = new Property<PropertyManager.OpenFile>("Load Positions");
            p33.setValue(ff);
            this.addProperty(p33);


            Property<Double> p = new Property<Double>("Simulation.K_REP");
            p.setValue(new Double(500000.));
            ((BarnesHutGraphDrawer) drawer).k_rep = 500000;
            this.addProperty(p);

            p = new Property<Double>("Simulation.K_ATT");
            p.setValue(new Double(100.));
            this.addProperty(p);

            p = new Property<Double>("Simulation.SPRING_LENGTH");
            p.setValue(new Double(15.));
            this.addProperty(p);

            p = new Property<Double>("Simulation.MAX_STEP");
            p.setValue(new Double(500.));
            ((BarnesHutGraphDrawer) drawer).max_step = 500.;
            this.addProperty(p);



            // cluster size and clustering based on attributes
            Property<Integer> p1 = new Property<Integer>("Clustering Weights.Cluster Size");
            p1.setValue(4);
            this.addProperty(p1);

            Property<Percent> p2 = new Property<Percent>("Clustering Weights.# Inflows    ");
            p2.setValue(new Percent(0.25));
            this.addProperty(p2);

            p2 = new Property<Percent>("Clustering Weights.# Outflows   ");
            p2.setValue(new Percent(0.25));
            this.addProperty(p2);

            p2 = new Property<Percent>("Clustering Weights.# Bytes Sent ");
            p2.setValue(new Percent(0.25));
            this.addProperty(p2);


            p2 = new Property<Percent>("Clustering Weights.# Bytes Recvd");
            p2.setValue(new Percent(0.25));
            this.addProperty(p2);

            Property<Button> p3 = new Property<Button>("Clustering Weights.Compute Clusters");
            p3.setValue(new Button());
            this.addProperty(p3);
            //ip address text box
            Property<String> p4 = new Property<String>("Searching Criteria.  IP address");
            p4.setValue("");
            this.addProperty(p4);


            //#Inflows textbox
            Property<Integer> p6 = new Property<Integer>("Searching Criteria.# Inflows");
            p6.setValue(new Integer(-1));
            this.addProperty(p6);
            //options for the criteria

            Property<Options> p10 = new Property<Options>("Searching Criteria.# InF criteria");
            Options o = new Options();
            o.options = new String[5];
            o.options[0] = "=";
            o.options[1] = "<";
            o.options[2] = ">";
            o.options[3] = "<=";
            o.options[4] = ">=";

            o.selectedIndex = 0;
            p10.setValue(o);
            this.addProperty(p10);




            Property<Integer> p7 = new Property<Integer>("Searching Criteria.# Outflows");
            p7.setValue(new Integer(-1));
            this.addProperty(p7);

            Property<Options> p11 = new Property<Options>("Searching Criteria.# OutF criteria");
            p11.setValue(o);
            this.addProperty(p11);


            Property<Integer> p8 = new Property<Integer>("Searching Criteria.# BytesSent");
            p8.setValue(new Integer(-1));
            this.addProperty(p8);

            Property<Options> p12 = new Property<Options>("Searching Criteria.# ByteS criteria");
            p12.setValue(o);
            this.addProperty(p12);


            Property<Integer> p9 = new Property<Integer>("Searching Criteria.# BytesRecvd");
            p9.setValue(new Integer(-1));
            this.addProperty(p9);

            Property<Options> p13 = new Property<Options>("Searching Criteria.# BytesR criteria");
            p13.setValue(o);
            this.addProperty(p13);


            //search button
            Property<Button> p5 = new Property<Button>("Searching Criteria.Search");
            p5.setValue(new Button());
            this.addProperty(p5);



        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    public <T> void propertyUpdated(Property p, T newvalue) {
        if (p.getName() == "Simulation.K_REP") {
            ((BarnesHutGraphDrawer) drawer).k_rep = (Double) newvalue;
        } else if (p.getName() == "Simulation.K_ATT") {
            ((BarnesHutGraphDrawer) drawer).k_att = (Double) newvalue;
        } else if (p.getName() == "Simulation.SPRING_LENGTH") {
            ((BarnesHutGraphDrawer) drawer).spring_length = (Double) newvalue;
        } else if (p.getName() == "Simulation.MAX_STEP") {
            ((BarnesHutGraphDrawer) drawer).max_step = (Double) newvalue;
        } else if (p.getName() == "Clustering Weights.Cluster Size") {
            this.setClusterSize(((Integer) newvalue).intValue());
            //System.out.println("cluster size: "+((Integer)newvalue).intValue());
        } else if (p.getName() == "Clustering Weights.# Inflows    ") {
            this.setClustWeightForInflows(((Percent) newvalue).getRatio());

            //System.out.println("#Inflows: "+ ((Percent) newvalue).getRatio());


        } else if (p.getName() == "Clustering Weights.# Outflows   ") {
            this.setClustWeightForOutflows(((Percent) newvalue).getRatio());
            //System.out.println("#Outflows: "+ ((Percent) newvalue).getRatio());
        } else if (p.getName() == "Clustering Weights.# Bytes Sent ") {
            this.setClustWeightForBytesSent(((Percent) newvalue).getRatio());
            //System.out.println("#Bytes Sent: "+ ((Percent) newvalue).getRatio());
        } else if (p.getName() == "Clustering Weights.# Bytes Recvd") {
            this.setClustWeightForBytesRecvd(((Percent) newvalue).getRatio());
            //System.out.println("#Bytes Received: "+ ((Percent) newvalue).getRatio());
        } else if (p.getName() == "Clustering Weights.Compute Clusters") {
            System.out.println("Computing node clusters...........................");
            //compute the node clusters
            this.computeNodeClusters();

        } else if (p.getName() == "Searching Criteria.  IP address") {

            //if the value is not empty, set the value and set it true otherwise set to false
            if (!(newvalue.toString()).isEmpty()) {
                this.setSrchIpAddress(true);
                this.setSrchIpAddressValue(newvalue.toString());
            } else {
                this.setSrchIpAddress(false);
            }



        } else if (p.getName() == "Searching Criteria.# Inflows") {
            //if the value  > -1, then set to true and set  value otherwise false
            if (((Integer) newvalue) > -1) {
                this.setSrchInflows(true);
                this.setSrchInflowsValue("" + (Integer) newvalue);
            } else {
                this.setSrchInflows(false);
            }
        } else if (p.getName() == "Searching Criteria.# InF criteria") {
            String options[] = ((Options) newvalue).options;
            int selectedIndex = ((Options) newvalue).selectedIndex;

            this.setSrchInflowsCriteria(options[selectedIndex]);
        } else if (p.getName() == "Searching Criteria.# Outflows") {
            //if the value  > -1, then set to true and set  value otherwise false
            if (((Integer) newvalue) > -1) {
                this.setSrchOutflows(true);
                this.setSrchOutflowsValue("" + (Integer) newvalue);
            } else {
                this.setSrchOutflows(false);
            }

        } else if (p.getName() == "Searching Criteria.# OutF criteria") {
            String options[] = ((Options) newvalue).options;
            int selectedIndex = ((Options) newvalue).selectedIndex;

            this.setSrchOutflowsCriteria(options[selectedIndex]);
        } else if (p.getName() == "Searching Criteria.# BytesSent") {
            //if the value  > -1, then set to true and set  value otherwise false
            if (((Integer) newvalue) > -1) {
                this.setSrchBytesSent(true);
                this.setSrchBytesSentValue("" + (Integer) newvalue);
            } else {
                this.setSrchBytesSent(false);
            }
        } else if (p.getName() == "Searching Criteria.# ByteS criteria") {
            String options[] = ((Options) newvalue).options;
            int selectedIndex = ((Options) newvalue).selectedIndex;

            this.setSrchBytesSentCriteria(options[selectedIndex]);
        } else if (p.getName() == "Searching Criteria.# BytesRecvd") {
            //if the value  > -1, then set to true and set  value otherwise false
            if (((Integer) newvalue) > -1) {
                this.setSrchBytesRecvd(true);
                this.setSrchBytesRecvdValue("" + (Integer) newvalue);
            }
            else{
                this.setSrchBytesRecvd(false);
            }

        } else if (p.getName() == "Searching Criteria.# BytesR criteria") {
            String options[] = ((Options) newvalue).options;
            int selectedIndex = ((Options) newvalue).selectedIndex;

            this.setSrchBytesRecvdCriteria(options[selectedIndex]);
        } else if (p.getName() == "Searching Criteria.Search") {
            this.searchForNodes();
        } else if (p.getName() == "Load Positions") {
            ArrayList<String> nodes = graph.getNodes();

            try {
                FileInputStream fstream = new FileInputStream(((PropertyManager.OpenFile) newvalue).path);
                DataInputStream in = new DataInputStream(fstream);
                BufferedReader br = new BufferedReader(new InputStreamReader(in));
                String s;
                while ((s = br.readLine()) != null) {
                    s = s.trim();

                    String[] split = s.split("\t");

                    if (split.length < 2) {
                        continue;
                    }

                    int index = nodes.indexOf(split[0].trim());
                    if (index < 0) {
                        String s2 = split[0].trim().toLowerCase();
                        for (int i = 0; i < nodes.size(); i++) {
                            String s1 = nodes.get(i).toLowerCase();
                            if (s1.equals(s2)) {
                                index = i;
                                break;
                            }
                        }
                        if (index < 0) {
                            continue;
                        }
                    }

                    String[] poss = split[1].split(",");
                    int x = (int) Double.parseDouble(poss[0]);
                    int y = -(int) Double.parseDouble(poss[1]);

                    if (drawer != null) {
                        drawer.setX(index, (int) (x * 1.335));
                        drawer.setY(index, (int) (y * 1.335));

                        this.setAspect(index, Points2DViewer.PointAspectType.RECT_LABEL_FIT);
                    }
                }

                in.close();
            } catch (Exception e) {
            }
        } else {
            super.propertyUpdated(p, newvalue);
        }
    }

    public void load(File f) {
        graph.fromGraphML(f);

        ArrayList<String> nodes = graph.getNodes();
        for (int i = 0; i < nodes.size(); i++) {
            Property<Integer> p = graph.nodeProperty(nodes.get(i), "x");
            if (p != null) {
                drawer.setX(i, p.getValue().intValue());
            } else {
                drawer.setX(i, 0);
            }

            p = graph.nodeProperty(nodes.get(i), "y");
            if (p != null) {
                drawer.setY(i, p.getValue().intValue());
            } else {
                drawer.setY(i, 0);
            }
        }
    }
    @Override
    public boolean mousepressed(int x, int y, int button) {
        super.mousepressed(x, y, button);
        int source[] = this.getEdgeSources();
        int target[] = this.getEdgeTargets();
            

        this.clearEdgeSelection();
        int minDistance = Integer.MAX_VALUE;
        for(int i=0;i< source.length;i++)
        {
            String sourceName =this.graph.getNodes().get(source[i]);
            String targetName =this.graph.getNodes().get(target[i]);
            int x1 = (int)this.drawer.getX( source[i]);
            int y1 = (int)this.drawer.getY(source[i]);
            int x2 = (int)this.drawer.getX(target[i]);
            int y2 = (int)this.drawer.getY(target[i]);
            double lineDistance = NetworkViewer.getLineDistance(x,y, x1, y1, x2, y2); 
            double midpointDistance =NetworkViewer.getMidPointDistance(x,y, x1, y1, x2, y2); 
            double edgeLength= NetworkViewer.GetPointDistance(x1, y1, x2, y2);
            double halfEdgeLength=edgeLength/2;
            if ( midpointDistance< halfEdgeLength && lineDistance < minDistance)
            {
                minDistance = (int) lineDistance;
                this.pickedEdge=i;
                
            }
        }
        if( this.pickedEdge >=0 && minDistance < 15)
        {   
            
            this.selectEdge(source[this.pickedEdge], target[this.pickedEdge]);
        }
        return false;
    }

    @Override
    public boolean mousemoved(int x, int y) {
        super.mousemoved(x, y);
        if(this.pickedEdge >= 0)
        {
            
            int source[] = this.getEdgeSources();
            int target[] = this.getEdgeTargets();
            int x1 = (int) this.drawer.getX(source[this.pickedEdge]);
            int y1 = (int) this.drawer.getY(source[this.pickedEdge]);
            int x2 = (int) this.drawer.getX(target[this.pickedEdge]);
            int y2 = (int) this.drawer.getY(target[this.pickedEdge]);
            double lineDistance = NetworkViewer.getLineDistance(x,y, x1, y1, x2, y2); 
            double midpointDistance =NetworkViewer.getMidPointDistance(x,y, x1, y1, x2, y2);
            double edgeLength= NetworkViewer.GetPointDistance(x1, y1, x2, y2);
            double halfEdgeLength=edgeLength/2;
            if ( midpointDistance< halfEdgeLength)
            {
                this.setToolTipText(this.getEdgeLabel(pickedEdge));
            }
        }
        return false;
    }

    public String getEdgeLabel(int index)
    {
        int source[] = this.getEdgeSources();
        int target[] = this.getEdgeTargets();
        String sourceName =this.graph.getNodes().get(source[index]);
        String targetName =this.graph.getNodes().get(target[index]);
        String id =sourceName+"-"+targetName;
        return this.edges.get(id).toString();
    }
    @Override
    protected void pointSelectionChanged() {
        
    }
    public static double GetPointDistance(double x1, double y1, double x2, double y2)
    {
        return Math.sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
    }
    public static double getLineDistance(double pointX, double pointY, double lineX1, double lineY1, double lineX2, double lineY2)
    {
        double distance = Math.abs((lineY1 - lineY2) * pointX + (lineX2 - lineX1) * pointY + lineX1 * lineY2 - lineX2 * lineY1) / NetworkViewer.GetPointDistance(lineX1, lineY1, lineX2, lineY2);
       
        return distance;
    }
    public static double getMidPointDistance(double pointX, double pointY, double lineX1, double lineY1, double lineX2, double lineY2)
    {
        //double distance = Math.abs((lineY1 - lineY2) * pointX + (lineX2 - lineX1) * pointY + lineX1 * lineY2 - lineX2 * lineY1) / NetworkViewer.GetPointDistance(lineX1, lineY1, lineX2, lineY2);
        double distance;
        double midX= (lineX1+lineX2)/2;
        double midY= (lineY1+lineY2)/2;
        distance=NetworkViewer.GetPointDistance(pointX, pointY, midX, midY);
        return distance;
    }
    
}
